package com.itheima.d2_reflect;

import org.junit.Test;
import java.lang.reflect.Constructor;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 目标：掌握获取类的构造器，并对其进行操作。
 *
 * 使用反射技术获取构造器对象并使用
 *  第一步：获得class对象
 *  第二步：获得Constructor对象
 *  第三步：创建对象
 *
 * 使用反射技术获取构造器对象并使用
 *  反射的第一步是先得到类对象，然后从类对象中获取类的成分对象。
 * Class类提供了从类中获取构造器的方法
 *      方法
 *  Constructor<?>[] getConstructors()                                  返回所有构造器对象的数组（只能拿public修饰的）
 *  Constructor<?>[] getDeclaredConstructors()                          返回所有构造器对象的数组，存在就能拿到
 *  Constructor<T> getConstructor(Class<?>... parameterTypes)           返回单个构造器对象（只能拿public修饰的）
 *  Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)   返回单个构造器对象，存在就能拿到
 *
 *  获取类构造器的作用:依然是初始化一个对象返回。
 *
 *  Constructor类中用于创建对象的方法
 *          符号
 * T newInstance(Object... initargs)            调用此构造器对象表示的构造器，并传入参数，完成对象的初始化并返回
 * public void  setAccessible(boolean flag)     设置为true,表示禁止检查访问控制（暴力反射）。一旦把这个代码设置为true，是不允许你去检查这个访问控制，也就是说私有的也能在外面访问，会破坏封装性
 *
 * 利用反射技术获取构造器对象的方式
 * getDeclaredConstructors()
 * getDeclaredConstructor (Class<?>... parameterTypes)
 * 反射得到的构造器可以做什么？
 * 依然是创建对象的
 * public newInstance(Object... initargs)
 * 如果是非public的构造器，需要打开权限（暴力反射），然后再创建对象
 * setAccessible(boolean)
 * 反射可以破坏封装性，私有的也可以执行了。
 *
 *
 */
public class Test2Constructor {
    @Test
    public void testGetConstructors(){
        // 1、反射第一步：必须先得到这个类的Class对象
        Class c = Cat.class;
        // 2、获取类的全部构造器，用构造器数组接着
        // Constructor[] constructors = c.getConstructors();    //只能获取public修饰的构造器
        Constructor[] constructors = c.getDeclaredConstructors();   //能够获取全部构造器
        // 3、遍历数组中的每个构造器对象
        for (Constructor constructor : constructors) {
            //getName可以拿到构造器的名字，getParameterCount可以拿到构造器里面有几个参数
            System.out.println(constructor.getName() + "--->"
            + constructor.getParameterCount());
        }
    }

    @Test
    public void testGetConstructor() throws Exception {
        // 1、反射第一步：必须先得到这个类的Class对象
        Class c = Cat.class;
        // 2、获取类的某个构造器：获取无参数构造器
        //Constructor constructor = c.getConstructor();   //只能获取public修饰的，指定的无参构造器，不如下面的好用
        Constructor constructor1 = c.getDeclaredConstructor();  //用构造器类型变量接着，有异常直接抛出去
        //Constructor<Cat> constructor1 = c.getDeclaredConstructor();   //反射一般不加泛型

        System.out.println(constructor1.getName() + "--->"
                + constructor1.getParameterCount());    //获取无参构造器里面的参数个数，为零

        //但是这个反射是非常强大的，一旦把这个代码设置为true，是不允许你去检查这个访问控制，也就是说私有的也能在外面访问，会破坏封装性
        //public void  setAccessible(boolean flag)设置为true,表示禁止检查访问控制（暴力反射）。一旦把这个代码设置为true，是不允许你去检查这个访问控制，也就是说私有的也能在外面访问，会破坏封装性
        constructor1.setAccessible(true); // 禁止检查访问权限

        /*
        通过类构造器对象来初始化类的对象返回:
        T newInstance(Object... initargs)    调用此构造器对象表示的构造器，并传入参数，完成对象的初始化并返回
        选择用获得的无参构造器对象来调用newInstance方法的时候，这个方法本质上还是调用构造器对象表示的这个构造器来进行调用，
            构造器需要多少参数并不能确定，所以这个方法用的是可变参数的形式，指定参数后就会去调用这个构造器对象表示的构造器，
                就可以完成对象的初始化并且返回

         Cat cat = constructor1.newInstance();会报错
         分析：调用这个newInstance方法的时候，本质上是调用Cat类的无参构造器得到一个猫对象，但是这个constructor1构造器
              并不知道自己是哪一个类的构造器，所以它的返回类型还是一个object类型，所以就会报错
         解决方法一，在前面的Constructor<Cat> constructor1 = c.getDeclaredConstructor();处声明一个Cat泛型，
                   就知道这个构造器就知道它是属于猫的构造器，那就不用强转了，但是反射一般不加泛型，原因是为了提高通用性
         解决方法二，强转：

         Cat cat = (Cat) constructor1.newInstance();因为目前cat类的无参构造器是private类型的，是不允许在外面访问的，
         所以会报错：IllegalAccessException，并且提示这个构造器是私有的private
        但是这个反射是非常强大的，一旦把这个代码设置为true，是不允许你去检查这个访问控制，也就是说私有的也能在外面访问，会破坏封装性
         */
        Cat cat = (Cat) constructor1.newInstance();
        System.out.println(cat);

        AtomicInteger a;

        System.out.println("---------------------------------");

        // 3、获取有参数构造器
        Constructor constructor2 =
                c.getDeclaredConstructor(String.class, int.class);  //String.class,代表要获取的有参构造器的第一个参数类型为string
        System.out.println(constructor2.getName() + "--->"
                + constructor2.getParameterCount());

        constructor2.setAccessible(true); // 禁止检查访问权限

        Cat cat2 = (Cat) constructor2.newInstance("叮当猫", 3);
        System.out.println(cat2);

    }
}





